/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once

#include "Hogshead.Common.h"

#include "Interfaces\IHObject.h"
#include "Interfaces\IComponent.h"
#include "Interfaces\IMessageReceiver.h"
#include "Interfaces\IMessage.h"
#include "Transform.h"


namespace Hogshead
{
	namespace Core
	{
		class Scene;
		class Prefab;
		class IGameObjectTreeHolder;
		/**
		* GameObject is a container class for a collective entity in the game. Each GameObject has a Transform that
		* locates it in three dimensional space. Each Scene has a hierarchy of GameObjects that are analogous 
		* to a Scene graph. Each GameObject contains a list of Components that define its characteristics. Each
		* GameObject can have a subtree of GameObjects.
		*
		* @see Transform
		* @see Scene
		* @see IComponent
		* @see IHObject
		* @see GameTimer
		* @see Prefab
		*/
		class GameObject : public IHObject, public IMessageReceiver
		{
		HObjectH(Hogshead::Core::GameObject)
		public:

			/**
			* Default Constructor, sets parent and scene to NULL, name is null string.
			*/
			GameObject();

			/**
			* Destructor, does nothing
			*/
			~GameObject();

			/**
			* assignment operator; copies in the the data in the other GameObject's Transform, Components,
			* and child GameObjects. 
			*
			* @param other The GameObject to copy from.
			*
			* @return Returns *this.
			*/
			GameObject& operator=(const GameObject& other);

			/**
			* initialize method; called at initialization time. Used to call initialize on our Transform, Components
			* and child GameObjects.
			*/
			void initialize();

			/**
			* update method; called during the update loop. Used to update all Components then all child GameObjects.
			*
			* @param in_gt The GameTimer for the running game which stores time information.
			*/
			void update(const GameTimer& in_gt);

			/**
			* shallow update; called during multithreaded update.  Only updates the components
			*/
			void shallowUpdate(const GameTimer& in_gt);

			/** 
			* getTransform method; used to get  a reference to this GameObject's Transform.
			*/
			Transform& getTransform();
		
			/**
			* name method; returns the name of this GameObject.
			*/
			const String& name() const;

			/**
			* id method; returns the unique id of this GameObject.
			*/
			int id() const;			
	
			/**
			* gameObjectCount method; returns the number of child GameObjects.
			*/
			int gameObjectCount() const;

			/**
			* getChildGameObject method; returns the child GameObject at the specified index.
			* @param in_index The index to grab from.
			* @return Returns the pointer to the requested child GameObject.
			*/
			GameObject* getChildGameObject(int in_index);

			/**
			* gameObjectSubtreeCount method; returns the number of child GameObjects in the subtree.
			*/
			int gameObjectSubtreeCount() const;

			/**
			* getParentGameObject method; returns this GameObject's parent.
			*/
			GameObject* getParentGameObject();

			/**
			* setParentGameObject method, sets this GameObject's parent
			*/
			void setParentGameObject(GameObject* in);
			
			/**
			* getComponentOfType method; returns the first found Component on this GameObject
			* of the type passed through the template. If no Component of the type is found,
			* NULL is returned.
			*/
			template<typename T> T* getComponentOfType()
			{
				for(int i = 0; i < _components.size(); i++)
					if(_components[i]->is<T>())
						return (T*)_components[i];
				return NULL;
			}

			/**
			* getComponentsOfType method; returns an Array1D of all Components found on this
			* GameObject of the type passed through the template. This will return an empty 
			* array if no Components of the specified type are found.
			*/
			template<typename T> Array1D<T*> getComponentsOfType(bool recurse = false)
			{
				int count = 0;

				if(!recurse)
				{
					for(int i = 0; i < _components.size(); i++)
					{
						if(_components[i]->is<T>())
							count++;
					}
					Array1D<T*> result(count);
				
					int index = 0;
					for(int i = 0; i < _components.size(); i++)
					{
						if(_components[i]->is<T>())
							result(index++) = (T*)_components[i];
					}
					return result;
				}
				else
				{
					//add my own
					Vector<T*> results(32);
					for(int i = 0; i < _components.size(); i++)
					{
						if(_components[i]->is<T>())
							results.add( _components[i]->as<T>() );
					}

					//add those on my children 
					Array1D<T*> sub;
					for( int k = 0; k < _child_game_objects.size(); k++ )
					{
						sub = _child_game_objects[k]->getComponentsOfType<T>(true);
						for(int j = 0; j < sub.length(); j++ )
							results.add( sub[j] );
					}
					
					Array1D<T*> result( results.size() );
					for( int h = 0; h < results.size(); h++ )
						result[h] = results[h];

					return result;
				}				
			}

			/** 
			* Recursivley search child game objects for game object with a given name
			*/
			GameObject* getChildByName(const String& in_name);

			/**
			* addComponent method; adds a new Component to this GameObject.
			* @param in_component The component to add.
			*/
			void addComponent(IComponent* in_component);


			/**
			* getComponent method; returns one of this GameObjects Components by index.
			*
			* @param in_index The index to grab from.1
			*
			* @return Returns the request Component.
			*/
			IComponent* getComponent(int in_index);

			/**
			* componentCount method; returns the number of Components attached to this GameObject.
			*/
			int componentCount() const;
			
			/**
			* readXML method; This method is called to load this GameObject in a Scene file.
			* It will copy in information about name, Transform information, if it should be
			* created from a Prefab, any Components, and any child GameObjects.
			*
			* @param in_xml A bock of XML stored in a string that should have a root element of this GameObject.
			* @param in_owner The Scene or Prefab that this GameObject belongs to.
			*
			* @return Returns a pointer to this GameObject.
			*/
			GameObject* readXML(const String& in_xml, IGameObjectTreeHolder* in_owner);

			/**
			* clone method; This method is called to get a new instance of a GameObject that is a copy
			* of this GameObject. The clone will have the same name, Transform data, Scene, copies of all
			* the same Components with mirrored data, and clones of all the same child GameObjects.
			*
			* @return Returns a pointer to the new instance of a copied GameObject.
			*/
			GameObject* clone() const;

			friend PrintStream& operator<<(PrintStream& in_stream, const GameObject& go);
			
			void setName(String in_name)
			{
				_name = in_name;
			}

			String getName() const {return _name;} 

			/**
			* addChildGameObject method; adds a new child game object this this GameObject.
			* Copies over the Scene to the child as well.
			*
			* @param in_game_object The child GameObject to add to the list of child GameObjects.
			*/

			void addChildGameObject(GameObject* in_game_object);

			IGameObjectTreeHolder* getOwner();
			/**
			* getScene method; returns a pointer to the Scene we belong to.
			* NOTE: this will return NULL if we belong to a Prefab.
			*/
			Scene* getScene();
		private:
			/**
			* Only removes the game object from the internal list.  
			*/
			void removeChildGameObject(GameObject* in_game_object);

		private:


			/** Adds the game object in  the addgameobjectmessage to the receiving game object and subsequently initializes it**/
			static void handle_add_game_object_message(IMessageReceiver*,IMessage*);
			/** This method takes the GameObject given and removes them from the receiver GameObject **/
			static void handle_remove_game_object_message(IMessageReceiver*,IMessage*);
			/** This method removes the GameObject given from the receiver GameObject, and then sends an AddGameObject message to the new parent **/
			static void handle_move_game_object_message(IMessageReceiver*,IMessage*);
			/** This method recursively sets the activation of this game object, child components and child game objects **/
			static void handle_set_activation_message(IMessageReceiver*,IMessage*);
			static void set_activity(GameObject*,bool);
			static void handle_set_transform(IMessageReceiver*, IMessage*);

			/**
			* copy method; This will copy in information from another GameObject into this GameObject.
			* Specifically it will copy name, Transform data, scene, copies Components with mirrored data,
			* and copies child GameObjects.
			*/
			GameObject& copy(const GameObject& in_other);

			void print_game_object(PrintStream& in_stream, int tree_depth) const;

			/**
			* setOwner method; This is called to set the IGameObjectTreeHolder to a GameObject and all of its children 
			* recursively. This is called by IGameObjectTreeHolder at the end of readXML. This ensures all GameObjects
			* copied from Prefabs have a correct IGameObjectTreeHolder pointer instead of NULL.
			*
			* @param in_owner The IGameObjectTreeHolder this GameObject is in.
			*/
			void setOwner(IGameObjectTreeHolder* in_owner);

			friend class IGameObjectTreeHolder;
			friend class Scene;
			friend class Transform;
			friend class IComponent;
			friend class Prefab;
			
			// name of the gameobject
			String _name;
			// immediate parent game object
			GameObject* _parent_game_object;
			// scene gameobject is member of
			IGameObjectTreeHolder* _owner;
			

			// the layer is the depth in the game tree this game object is
			// root is 0, second layer is 1, etc
			int _layer;
			// if active, game object and components will update
			// when inactive, will not udpate and child components will ignore messages
			bool _active;

			Vector<GameObject*> _child_game_objects;
			Vector<IComponent*> _components;

			Transform _transform;

			int _id;
			static int _count;


		};
	}
}